package com.demo.aop;

import com.demo.aop.advice.Advice;
import com.demo.aop.advisor.Advisor;
import com.demo.aop.pointcut.Pointcut;
import com.demo.bean.BeanFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

public class JDKAopProxy implements AopProxy,InvocationHandler {

    private Class[] interfaces;
    private ClassLoader classLoader;
    private List<Advisor> advisorList;
    private Object target;
    private BeanFactory beanFactory;


    public JDKAopProxy(ClassLoader classLoader, Class[] interfaces, List<Advisor> advisorList, Object target, BeanFactory beanFactory){
        this.classLoader = classLoader;
        this.interfaces = interfaces;
        this.advisorList = advisorList;
        this.target = target;
        this.beanFactory = beanFactory;
    }

    @Override
    public Object getProxy() throws Exception {
        return getProxy(classLoader);
    }

    @Override
    public Object getProxy(ClassLoader classLoader) throws Exception {
        return Proxy.newProxyInstance(classLoader,interfaces,this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        List<Advice> matchAdvice = getAllAdvice(method);
        InvocationHandleMethodChain invocationHandleMethodChain = new InvocationHandleMethodChain(matchAdvice,target,method,args);
        return invocationHandleMethodChain.invoke();
    }

    private List<Advice> getAllAdvice(Method method) throws Exception {
        if(advisorList == null){
            return null;
        }
        List<Advice> adviceList = new ArrayList<>(advisorList.size());
        for (Advisor advisor : advisorList) {
            Pointcut pointcut = advisor.getPointcut();
            if(!pointcut.matchClass(target.getClass())){
                continue;
            }
            if(pointcut.matchMethod(target.getClass(),method)){
                adviceList.add(beanFactory.getBean(advisor.getAdviceBeanName(), Advice.class));
            }
        }
        return adviceList;
    }
}
